/**
 * @Author: Jiang Zhongxun
 * @Date: 2024-10-03 20:52:32
 * @FilePath: /practiceProject/src/server/server.c
 * @Description:
 */
#include "../../include/server/server.h"
#include "../../include/db/dbDefinition.h"
#include "../../include/db/dbOperation.h"
#include "../../include/server/adminSys.h"
#include "../../include/server/loginAndRegister.h"
#include "../../include/server/userSys.h"
#include "../../include/utils/utils.h"

const int PORT = 6666; // 端口号

extern sqlite3 *db;

// 定义变量，标识用户登录情况【登录状态【100用户不存在，200密码错误, 300登录成功】】
int loginStatus = 0;
// 定义变量，标识用户注册情况【注册状态【100用户不存在，200密码错误, 300登录成功】】
int registerStatus = 0;

/**
 * @description: 服务端通信
 * @param {int} newfd 套接字
 * @return {*} 成功返回0，失败返回-1
 */
int tcpServerCom(int newfd)
{
    // 判断入参
    if (newfd < 0)
    {
        printf("newfd is error");
        return -1;
    }
    int recvCount;
    int totalReceived = 0; // 接收到的总数据量
    int temp_res;
    char msgToClient[128]; // 接收客户端消息
    char buf[64] = {0};
    char usernameAndPasswd[64] = {0};
    bzero(buf, sizeof(buf));

    // int maxAttempts = 10; // 限制最大尝试次数
    // int attempts = 0;
    // while (totalReceived < sizeof(buf) && attempts < maxAttempts)
    while (totalReceived < sizeof(buf))
    {
        // 接收客户端发来的：业务种类@业务名称
        // 计算出还剩下多少字节可以接收，确保我们不会超出缓冲区的大小
        // 减少接收的字节数，确保不会超过缓冲区大小
        int bytesLeft = sizeof(buf) - totalReceived;
        recvCount = recv(newfd, buf + totalReceived, bytesLeft, 0);
        // recvCount = recv(newfd, buf, sizeof(buf), 0);
        if (recvCount > 0)
        {
            totalReceived += recvCount;
            // 当接收到的数据量达到缓冲区大小时，手动添加结束符
            if (totalReceived >= sizeof(buf))
            {
                buf[sizeof(buf) - 1] = '\0'; // 强制结束符
            }
            // buf[totalReceived] = '\0'; // 确保字符串以 '\0' 结束
            // attempts = 0; // Reset attempts on successful receive
            printf("recvCount: %d, totalReceived: %d\n", recvCount, totalReceived);
        }
        else if (0 == recvCount)
        {
            printf("server客户端选择退出!\n");
            return -1;
        }
        else if (recvCount < 0)
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                // attempts++;
                // 没有数据可读取，继续等待其他事件
                usleep(1000); // 等待一小段时间后再继续尝试接收
                continue;     // 继续下一轮循环，等待更多数据
            }
            else
            {
                perror("server init recv error");
                return -1; // 处理其他错误
            }
        }
    }
    // 这里需要确保你在后续操作中处理字符串时避免超出
    printf("server init Final buf: %s\n", buf); // 打印最终的 buf 内容
    printf("server init totalReceived = %d\n", totalReceived);
    printf("server接收业务种类@业务名称,成功! buf = %s\n", buf);
    // 解析数据
    char *res[5] = {NULL};
    if (totalReceived == 0 || buf[0] == '\0')
    {
        printf("接收到的 buf 为空，终止操作。\n");
        return -1;
    }
    parseStringUtil(buf, res);
    printf("server init 业务数据解析成功!\n");
    // 按照业务名称进行模块处理
    // 登录业务
    if (0 == strncasecmp(res[1], "login", 5))
    {
        // 接收客户端发来的用户名与密码
        totalReceived = 0;
        bzero(usernameAndPasswd, sizeof(usernameAndPasswd));
        while (totalReceived < sizeof(usernameAndPasswd))
        {
            int bytesleft = sizeof(usernameAndPasswd) - totalReceived;
            // recvCount = recv(newfd, usernameAndPasswd, sizeof(usernameAndPasswd), 0);
            recvCount = recv(newfd, usernameAndPasswd + totalReceived, bytesleft, 0);
            if (recvCount > 0)
            {
                totalReceived += recvCount;
                // 当接收到的数据量达到缓冲区大小时，手动添加结束符
                if (totalReceived >= sizeof(usernameAndPasswd))
                {
                    usernameAndPasswd[sizeof(usernameAndPasswd) - 1] = '\0'; // 强制结束符
                }
                // buf[totalReceived] = '\0'; // 确保字符串以 '\0' 结束
                // attempts = 0; // Reset attempts on successful receive
                printf("recvCount: %d, totalReceived: %d\n", recvCount, totalReceived);
            }
            else if (0 == recvCount)
            {
                printf("server客户端选择退出!\n");
                return -1;
            }
            else if (recvCount < 0)
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    // 没有数据可读取，继续等待其他事件
                    usleep(1000); // 等待一小段时间后再继续尝试接收
                    continue;     // 继续下一轮循环，等待更多数据
                }
                else
                {
                    perror("server login recv error");
                    return -1; // 处理其他错误
                }
            }
            totalReceived += recvCount;
        }
        // 这里需要确保你在后续操作中处理字符串时避免超出
        printf("server login Final buf: %s\n", usernameAndPasswd); // 打印最终的 buf 内容
        printf("server login totalReceived = %d\n", totalReceived);
        printf("usernameAndPasswd = %s\n", usernameAndPasswd);
        // 解析数据
        char *loginRes[5] = {NULL};
        if (totalReceived == 0 || usernameAndPasswd[0] == '\0')
        {
            printf("接收到的 buf 为空，终止操作。\n");
            return -1;
        }
        parseStringUtil(usernameAndPasswd, loginRes);
        printf("登录: userName = %s, passwd = %s\n", loginRes[1], loginRes[2]);
        // 调用登录接口

        temp_res = userLogin(db, loginRes[1], loginRes[2], &loginStatus);
        bzero(msgToClient, sizeof(msgToClient));
        if (temp_res != 0)
        {
            sprintf(msgToClient, "%s@%s@%d", res[1], "Sorry", loginStatus);
        }
        else
        {
            sprintf(msgToClient, "%s@%s@%d", res[1], "Lucky", loginStatus);
        }
        // 将登录结果发送给客户端
        send(newfd, msgToClient, sizeof(msgToClient), 0);
        // 登录成功之后，接收用户或管理员的下一步指令，进行操作
        if (300 == loginStatus)
        {
            if (0 == strcmp(loginRes[1], "root"))
            {
                // 管理员登录成功
                while (1)
                {
                    int ret = adminTcpServerCom(newfd, db);
                    if (ret != 0)
                    {
                        break;
                    }
                }
            }
            else
            {
                // 普通用户登录成功
                // recvFlag = 1;
                while (1)
                {
                    int ret = userTcpServerCom(newfd, db);
                    // recvFlag = 0;
                    if (ret != 0)
                    {
                        break;
                    }
                }
            }
        }
        else
        {
            printf("【%s】登录失败!\n", loginRes[1]);
            return -1;
        }
    }
    // 注册业务
    else if (0 == strncasecmp(res[1], "register", 8))
    {
        totalReceived = 0;
        bzero(usernameAndPasswd, sizeof(usernameAndPasswd));
        while (totalReceived < sizeof(usernameAndPasswd))
        {
            int bytesleft = sizeof(usernameAndPasswd) - totalReceived;
            // 接收客户端发来的用户名与密码
            // recvCount = recv(newfd, usernameAndPasswd, sizeof(usernameAndPasswd), 0);
            recvCount = recv(newfd, usernameAndPasswd + totalReceived, bytesleft, 0);
            if (recvCount > 0)
            {
                totalReceived += recvCount;
                // 当接收到的数据量达到缓冲区大小时，手动添加结束符
                if (totalReceived >= sizeof(usernameAndPasswd))
                {
                    usernameAndPasswd[sizeof(usernameAndPasswd) - 1] = '\0'; // 强制结束符
                }
                // buf[totalReceived] = '\0'; // 确保字符串以 '\0' 结束
                // attempts = 0; // Reset attempts on successful receive
                printf("recvCount: %d, totalReceived: %d\n", recvCount, totalReceived);
            }
            else if (0 == recvCount)
            {
                printf("server客户端选择退出!\n");
                return -1;
            }
            else if (recvCount < 0)
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                {
                    // 没有数据可读取，继续等待其他事件
                    usleep(1000); // 等待一小段时间后再继续尝试接收
                    continue;     // 继续下一轮循环，等待更多数据
                }
                else
                {
                    perror("server register recv error");
                    return -1; // 处理其他错误
                }
            }
            totalReceived += recvCount;
        }
        // 这里需要确保你在后续操作中处理字符串时避免超出
        printf("server register Final buf: %s\n", usernameAndPasswd); // 打印最终的 buf 内容
        printf("server register totalReceived = %d\n", totalReceived);
        printf("usernameAndPasswd = %s\n", usernameAndPasswd);
        // 解析数据
        char *registerRes[5] = {NULL};
        if (totalReceived == 0 || buf[0] == '\0')
        {
            printf("接收到的 buf 为空，终止操作。\n");
            return -1;
        }
        parseStringUtil(usernameAndPasswd, registerRes);
        printf("注册: userName = %s, passwd = %s\n", registerRes[1], registerRes[2]);

        // 调用注册接口
        temp_res = userRegister(db, registerRes[1], registerRes[2], &registerStatus);
        bzero(msgToClient, sizeof(msgToClient));
        if (temp_res != 0)
        {
            sprintf(msgToClient, "%s@%s@%d", res[1], "Sorry", registerStatus);
        }
        else
        {
            sprintf(msgToClient, "%s@%s@%d", res[1], "Lucky", registerStatus);
        }
        // 将注册结果发送给客户端
        send(newfd, msgToClient, sizeof(msgToClient), 0);
        printf("注册结果发送给客户端, 成功!\n");
        // 登录成功之后，接收用户或管理员的下一步指令，进行操作
        if (300 == registerStatus)
        {
            printf("【%s】注册成功!\n", registerRes[1]);
        }
        else
        {
            printf("【%s】注册失败!\n", registerRes[1]);
            return -1;
        }
    }
    return 0;
}

// 线程池跟任务队列，还是会有问题
#if 0
/* 初始化线程池 */
void init_thread_pool(ThreadPool *pool)
{
    pool->task_queue.front = 0;
    pool->task_queue.rear = 0;
    pthread_mutex_init(&pool->task_queue.lock, NULL);
    pthread_cond_init(&pool->task_queue.notify, NULL);
    for (int i = 0; i < THREAD_POOL_SIZE; i++)
    {
        pthread_create(&pool->threads[i], NULL, worker_thread, pool);
    }
}

/* 销毁线程池 */
void destroy_thread_pool(ThreadPool *pool)
{
    for (int i = 0; i < THREAD_POOL_SIZE; i++)
    {
        pthread_join(pool->threads[i], NULL);
    }
    pthread_mutex_destroy(&pool->task_queue.lock);
    pthread_cond_destroy(&pool->task_queue.notify);
}

/* 任务队列入队（循环队列） */
void enqueue(TaskQueue *queue, Task task)
{
    pthread_mutex_lock(&queue->lock);
    queue->tasks[queue->rear] = task;
    queue->rear = (queue->rear + 1) % QUEUE_SIZE;
    pthread_mutex_unlock(&queue->lock);
    pthread_cond_signal(&queue->notify);
}

/* 任务队列出队 */
Task dequeue(TaskQueue *queue)
{
    Task task;
    pthread_mutex_lock(&queue->lock);
    task = queue->tasks[queue->front];
    queue->front = (queue->front + 1) % QUEUE_SIZE;
    pthread_mutex_unlock(&queue->lock);
    return task;
}

/* 线程池中的工作线程函数 */
void *worker_thread(void *arg)
{
    ThreadPool *pool = (ThreadPool *)arg;
    while (1)
    {
        pthread_mutex_lock(&pool->task_queue.lock);
        while (pool->task_queue.front == pool->task_queue.rear)
        {
            pthread_cond_wait(&pool->task_queue.notify, &pool->task_queue.lock);
        }
        Task task = dequeue(&pool->task_queue);
        pthread_mutex_unlock(&pool->task_queue.lock);
        // 处理客户端连接
        tcpServerCom(task.client_fd);
        close(task.client_fd);
    }
    return NULL;
}
#endif

// ===========================================================  处理客户端通信的线程函数【无问题】 =========================================================
#if 1
// 处理客户端通信的线程函数
void *handle_client(void *arg)
{
    while (1)
    {
        int client_fd = *((int *)arg);
        // 调用封装的通信处理函数
        int result = tcpServerCom(client_fd);
        // 根据通信结果关闭连接
        if (result == -1)
        {
            printf("客户端fd %d 处理完毕, 关闭连接\n", client_fd);
            close(client_fd); // 关闭客户端连接
            break;
        }
    }
    return NULL;
}
#endif
// ===========================================================  处理客户端通信的线程函数【无问题】 =========================================================

int main(int argc, char const *argv[])
{
// 初始化数据库与数据库表
// 下面一行代码，仅在单独测试时开放，因为已经在创建守护进程的函数中做了调用
#if 1
    db = databaseInit();
#endif

    int sockfd, temp_res;
    // 1. 创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        return -1;
    }
    printf("socket ok!\n");

    // 设置端口号可以复用
    int value = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));

    // 2. 绑定自己的IP地址和端口号。设置服务器的IP地址和端口号
    struct sockaddr_in serverAddr;          // 定义地址信息结构体变量
    bzero(&serverAddr, sizeof(serverAddr)); // 清空
    serverAddr.sin_family = AF_INET;        // 地址族
    // serverAddr.sin_port = htons((short)atoi(argv[2])); // 端口号
    // serverAddr.sin_addr.s_addr = inet_addr(argv[1]);   // IP地址
    serverAddr.sin_port = htons(PORT);              // 端口号
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); // IP地址
    // 3. 绑定
    temp_res = bind(sockfd, (struct sockaddr *)&serverAddr, (socklen_t)sizeof(serverAddr));
    if (temp_res < 0)
    {
        perror("bind error");
        close(sockfd);
        return -1;
    }
    printf("bind ok!\n");
    // 4. 创建监听队列
    temp_res = listen(sockfd, 128);
    if (temp_res < 0)
    {
        perror("listen error");
        close(sockfd);
        return -1;
    }
    printf("listening...\n");
    // 定义存储新连接的客户端的IP地址和端口号
    struct sockaddr_in clientAddr;
    bzero(&clientAddr, sizeof(clientAddr));
    socklen_t len = sizeof(clientAddr);
    int newfd;

    // 设置为非阻塞模式
    int flag = fcntl(sockfd, F_GETFL, 0);
    fcntl(sockfd, F_SETFL, flag | O_NONBLOCK);
    // 将套接字设置为阻塞模式
    // int flag = fcntl(sockfd, F_GETFL, 0);
    // fcntl(sockfd, F_SETFL, flag & ~O_NONBLOCK);

    // 创建 epoll 实例
    int epfd = epoll_create(MAX_EVENTS);
    if (epfd < 0)
    {
        perror("epoll create error");
        close(sockfd);
        return -1;
    }

    // 将监听套接字加入到 epoll 事件列表
    struct epoll_event ev, events[MAX_EVENTS];
    ev.events = EPOLLIN; // 监听读事件
    ev.data.fd = sockfd; // 监听的文件描述符
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev) < 0)
    {
        perror("epoll_ctl error");
        close(sockfd);
        return -1;
    }

    // 定义设置超时时间
    int timeout = 10000; // 单位是毫秒
    int timeout_count = 0;
// int max_timeout_count = 10; // 超时10次后退出

// 线程池跟任务队列，还是会有问题
#if 0
    // 初始化线程池
    ThreadPool thread_pool;
    init_thread_pool(&thread_pool);
#endif

    // 事件循环
    int nfds;
    while (1)
    {
        // 等待事件就绪
        // 设置超时时间
        nfds = epoll_wait(epfd, events, MAX_EVENTS, timeout);
        if (nfds < 0)
        {
            perror("epoll_wait error");
            break;
        }
        else if (0 == nfds)
        {
            printf("epoll wait timeout!!!!!!\n");
            timeout_count++;
            // if (timeout_count >= max_timeout_count)
            // {
            //     printf("连续超时 %d 次，退出循环!!!\n", max_timeout_count);
            //     break;
            // }
            continue;
        }
        else
        {
            // 复位超时计数器
            timeout_count = 0;
            printf("data is available~~~~~~\n");
        }

        // 遍历所有就绪的事件
        for (size_t i = 0; i < nfds; i++)
        {
            if (sockfd == events[i].data.fd)
            {
                // 监听套接字有新的客户端连接
                // 5. 等待建立与新的客户端连接
                // newfd = accept(sockfd, NULL, NULL);
                newfd = accept(sockfd, (struct sockaddr *)&clientAddr, &len);
                if (newfd < 0)
                {
                    perror("accept error");
                    continue;
                    // close(sockfd);
                    // return -1;
                }
                printf("accept ok! 新连接的客户端IP = %s, 端口号Port = %d, newfd = %d 成功连接到服务器!\n",
                       inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port), newfd);

                // 设置新连接为非阻塞模式
                flag = fcntl(newfd, F_GETFL, 0);
                fcntl(newfd, F_SETFL, flag | O_NONBLOCK);
                // 将套接字设置为阻塞模式
                // flag = fcntl(newfd, F_GETFL, 0);
                // fcntl(newfd, F_SETFL, flag & ~O_NONBLOCK);

                // 将新连接的描述符加入到 epoll 事件列表
                ev.events = EPOLLIN | EPOLLET; // 边沿触发，监听读事件
                // ev.events = EPOLLIN; // 边沿触发，监听读事件
                ev.data.fd = newfd;
                if (epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &ev) < 0)
                {
                    perror("epoll_ctl error");
                    close(newfd);
                    // return -1;
                    continue;
                }

// ========================================================= EPOLL 中使用子线程处理连接【无问题】=====================================================
#if 1
                // 创建子线程处理客户端通信
                pthread_t thread;
                // 动态分配内存存储文件描述符
                int *client_fd = malloc(sizeof(int));
                *client_fd = newfd;
                if (pthread_create(&thread, NULL, handle_client, client_fd) != 0)
                {
                    perror("创建线程失败");
                    close(newfd);
                    // 释放已分配的内存
                    free(client_fd);
                }
                else
                {
                    // 设置子线程分离，自动回收资源
                    pthread_detach(thread);
                }
#endif
// ========================================================= EPOLL 中使用子线程处理连接【无问题】=====================================================

// 线程池跟任务队列，还是会有问题
#if 0
                // 将新连接的文件描述符交给线程池处理
                Task task;
                task.client_fd = newfd;
                enqueue(&thread_pool.task_queue, task);
#endif

// ========================================================= EPOLL 中使用子进程处理连接【无问题】=====================================================
#if 0
                // 创建子进程处理该连接
                pid_t pid = fork();
                if (pid == 0) // 子进程
                {
                    // 子进程关闭监听套接字，只处理客户端的连接
                    close(sockfd);
                    // 子进程中处理客户端通信的循环
                    while (1)
                    {
                        int ret = tcpServerCom(newfd); // 处理客户端通信
                        // 判断是否需要退出，比如客户端关闭连接或通信结束
                        if (ret == 0)
                        {
                            printf("客户端关闭连接, newfd = %d\n", newfd);
                            continue;
                        }
                        if (ret < 0)
                        {
                            // printf("客户端fd = %d, 业务处理完毕!\n", newfd);
                            // // 将 fd 从 epoll 监听中移除
                            // if (epoll_ctl(epfd, EPOLL_CTL_DEL, newfd, NULL) < 0)
                            // {
                            //     perror("epoll_ctl del error");
                            // }
                            // perror("tcpServerCom error");
                            break; // 退出子进程的通信循环
                        }
                    }
                    // 父进程关闭客户端套接字，继续监听新的连接
                    close(newfd);
                    exit(0); // 子进程退出
                }
                else if (pid > 0) // 父进程
                {
                    // 父进程关闭客户端套接字，继续监听新的连接
                    close(newfd);
                }
                else
                {
                    perror("fork error");
                    close(newfd);
                }
// ========================================================= EPOLL 中使用子进程处理连接【无问题】=====================================================
#endif
            }
#if 0
            else
            {
                // 通信套接字有数据可读
                // 6. 通信
                // int clientfd = events[i].data.fd;
                // ============================【原epoll相关代码】==================================
                // int ret = tcpServerCom(clientfd);
                // if (ret < 0)
                // {
                //     printf("客户端fd = %d, 业务处理完毕!\n", clientfd);
                //     // 将 fd 从 epoll 监听中移除
                //     if (epoll_ctl(epfd, EPOLL_CTL_DEL, clientfd, NULL) < 0)
                //     {
                //         perror("epoll_ctl del error");
                //     }
                //     close(clientfd);
                // }
                // ============================【原epoll相关代码】==================================

                // ============================【线程池相关代码】==================================
                // // 创建任务
                // Task task;
                // task.client_fd = clientfd;
                // // task.db = db;
                // // 加锁并添加任务到队列
                // // pthread_mutex_lock(&queueMutex);
                // enqueue(taskQueue, task);
                // // pthread_mutex_unlock(&queueMutex);
                // // 唤醒工作线程
                // // pthread_cond_signal(&queueCond);
                // ============================【线程池相关代码】==================================
            }
#endif
        }
    }

    // 6. 关闭套接字
    close(sockfd);
    close(epfd);

// 线程池跟任务队列，还是会有问题
#if 0
    // 销毁线程池
    destroy_thread_pool(&thread_pool);
#endif

    return 0;
}
